package org.example.confirm.producer.config;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * @PostConstruct注解是Java中的一个标准注解，它用于指定在对象创建之后立即执行的方法。
     * 当使用依赖注入（如Spring框架）或者其他方式创建对象时，@PostConstruct注解可以确保在对象完全初始化之后，执行相应的方法。
     * 使用@PostConstruct注解的方法必须满足以下条件：
     *
     * 1. 方法不能有任何参数。
     * 2. 方法必须是非静态的。
     * 3. 方法不能返回任何值。
     *
     * 当容器实例化一个带有@PostConstruct注解的Bean时，它会在调用构造函数之后，并在依赖注入完成之前
     * 调用被@PostConstruct注解标记的方法。这样，我们可以在该方法中进行一些初始化操作，比如读取配置文件、建立数据库连接等。
     */
    @PostConstruct
    public void init() {
        /**
         * 对RabbitTemplate的功能进行增强，因为回调函数所在对象必须设置到RabbitTemplate对象中才能生效
         * 原本RabbitTemplate对象并没有生产者端消息确认的功能，要给它设置对应的组件才可以
         * 而设置对应的组件，需要调用RabbitTemplate对象下面两个方法
         */
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
    }

    /**
     * 方法功能：确认消息是否发送到交换机
     * 所属接口：ConfirmCallback
     * 接口所属类：RabbitTemplate
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        //ack参数值为true：表示消息成功发送到了交换机
        //ack参数值为false：表示消息发送到交换机失败
        if (ack) {
            log.info("消息发送到交换机成功！数据：" + correlationData);
        } else {
            log.info("消息发送到交换机失败！数据：" + correlationData + " 原因：" + cause);
        }
    }

    /**
     * 方法功能：确认消息是否发送到队列
     * 所属接口：ReturnsCallback
     * 接口所属类：RabbitTemplate
     * 注意：returnedMessage()方法仅在消息没有发送到队列时调用
     */
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        log.info("消息主体: " + new String(returned.getMessage().getBody()));
        log.info("应答码: " + returned.getReplyCode());
        log.info("描述：" + returned.getReplyText());
        log.info("消息使用的交换器 exchange : " + returned.getExchange());
        log.info("消息使用的路由键 routing : " + returned.getRoutingKey());
    }
}
